﻿// IPC Library
// Interprocess Communication Library
// Copyright(C)2008 Bill Tudor
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.ComponentModel;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

namespace IPCLibrary
{
	/// <summary>
	/// A mailslot server.
	/// </summary>
	/// <remarks>
	/// A mailslot server collects messages (datagrams) from mailslot clients. The
	/// messages may be read.
	/// </remarks>
	/// <seealso cref="IPCLibrary.MailslotClient"/>
	/// <seealso cref="IPCLibrary.MailslotBase"/>
	/// <seealso cref="IPCLibrary.ReadCompletedEventArgs"/>
	public class Mailslot : MailslotBase
	{
		private int _maxMessageSize;
		private int _nextMessageSize;
		private int _messageCount;

		/// <summary>
		/// Raised when an asynchronous read operation is completed.
		/// <para></para>
		/// <para>See <see cref="ReadCompletedEventArgs"/> for more information.</para>
		/// </summary>
		public event EventHandler<ReadCompletedEventArgs> ReadCompleted;
		/// <summary>
		/// Maximum allowed message size.
		/// </summary>
		public int MaxMessageSize
		{
			get
			{
				UpdateMailslotInfo();
				return _maxMessageSize;
			}
		}
		/// <summary>
		/// Size of the next message in the mailslot.
		/// </summary>
		public int NextMessageSize
		{
			get
			{
				UpdateMailslotInfo();
				return _nextMessageSize;
			}
		}
		/// <summary>
		/// Number of messages in the mailslot.
		/// </summary>
		public int MessageCount
		{
			get
			{
				UpdateMailslotInfo();
				return _messageCount;
			}
		}
		/// <summary>
		/// Creates a mailslot server.
		/// </summary>
		/// <param name="mailslotName">Mailslot name.</param>
		public Mailslot(string mailslotName)
			: base(mailslotName) { }
		/// <summary>
		/// Creates a mailslot server.
		/// </summary>
		/// <param name="mailslotScope">Mailslot scope.</param>
		/// <param name="mailslotName">Mailslot name.</param>
		/// <remarks>
		/// The mailslot scope can be a hostname, a domain name, or one of two
		/// special characters: use "*" for the current system's primary domain, or
		/// use "." to represent the current computer.
		/// <para></para>
		/// <para>For IPC between processes on the same machine, use ".".</para>
		/// <para>For IPC between machines in the current domain, use "*".</para>
		/// <para>For IPC between the current host and a specific mailslot server, use the hostname
		/// of the remote server.</para>
		/// <para>For IPC between the current host and any number of servers on any computer in the
		/// host's domain, use either the "*" special name or the domain name".</para>
		/// </remarks>
		public Mailslot(string mailslotScope, string mailslotName)
			: base(mailslotScope, mailslotName) { }
		/// <summary>
		/// Create the mailslot.
		/// </summary>
		public void Create()
		{
			CreateServer();
		}
		// Sync methods

		/// <summary>
		/// Read data from the mailslot.
		/// </summary>
		/// <returns>Mailsot data.</returns>
		public byte[] Read()
		{
			return ReadBytes();
		}
		/// <summary>
		/// Read message from the mailslot.
		/// </summary>
		/// <returns>Mailslot message.</returns>
		public string ReadMessage()
		{
			return ReadMessage(Encoding.ASCII);
		}
		/// <summary>
		/// Read message from the mailslot.
		/// </summary>
		/// <param name="characterEncoding">Character encoding to use.</param>
		/// <returns>Mailsot message.</returns>
		public string ReadMessage(Encoding characterEncoding)
		{
			return characterEncoding.GetString(ReadBytes());
		}

		/// <summary>
		/// Read a message asynchronously.
		/// </summary>
		/// <returns>True is successfully queued.</returns>
		/// <remarks>
		/// The ReadCompleted event is raised when this method completes. The message data
		/// is contained within the event arguments.
		/// </remarks>
		public bool ReadAsync()
		{
			return ThreadPool.QueueUserWorkItem(new WaitCallback(readAsyncCallback));
		}
		private void readAsyncCallback(object state)
		{
			try
			{
				byte[] result = ReadBytes();

				if (ReadCompleted != null)
				{
					ReadCompleted(this, new ReadCompletedEventArgs(result));
				}
			}
			catch (IPCException ex)
			{
				Trace.WriteLine(ex);
			}
		}
		private void UpdateMailslotInfo()
		{
			int maxSize = 0, nextSize = 0, count = 0, timeout = 0;
			if (GetMailslotInfo(ref maxSize, ref nextSize, ref count, ref timeout))
			{
				if (maxSize == 0)
				{
					_maxMessageSize = NativeMethods.MAX_MAILSLOT_SIZE;
				}
				else
				{
					_maxMessageSize = maxSize;
				}
				_nextMessageSize = nextSize;
				_messageCount = count;
			}
			else
			{
				_maxMessageSize = NativeMethods.MAX_MAILSLOT_SIZE;
				_nextMessageSize = -1;
				_messageCount = 0;
			}
		}
	}
}
